#include "xParser.h"
#include "xLogManager.h"
#include "xMath.h"
#include <rmxftmpl.h>
#include <rmxfguid.h>
#include "XEngine.h"
#include "XRenderSystem.h"
#include "xMeshManager.h"
#include "xHelp.h"
#include "xSkin.h"
#include "xShaderManager.h"
#include "xMaterial.h"
#include "xTextureManager.h"

namespace XE
{


	xParser::xParser()
		:m_File(NULL),m_pCurrentBone(NULL),m_pSkinInfor(NULL),
		m_pAnisetCollect(NULL),m_pCurrentAniSet(NULL),m_pCurrentAni(NULL),
		m_Node(NULL),m_pEntity3D(NULL),m_pCurrentEntity(NULL),
		m_pCurrentMesh(NULL),m_pRenderScheme(NULL)

	{

	}



	xParser::~xParser()
	{
		SafeRelease(m_File)
			//  SafeDelete(m_pSkeleton)
			//	SafeDelete(m_pAniSet)
			//	SafeDelete(m_pMesh)


	}

	HRESULT xParser::LoadFile(const xString& fileName)
	{
		if(m_File==NULL)
		{
			FL(D3DXFileCreate(&m_File),"CreateDiretxFileFail")
				FL(m_File->RegisterTemplates(D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES),\
				"RegisterTemplate Fail:D3DRM_XTEMPLATES")
		}

		//----------register Template---------

		HRESULT hr = 	m_File->RegisterTemplates( XSKINEXP_TEMPLATES,sizeof(XSKINEXP_TEMPLATES)-1);
		FL(m_File->RegisterTemplates(szTemplates,sizeof(szTemplates)-1),"Register TemplaterFail")



			LPD3DXFILEENUMOBJECT pEnumObj=NULL;
		FL(m_File->CreateEnumObject((void*)fileName.c_str(),DXFILELOAD_FROMFILE,&pEnumObj),\
			"Create EnumObjeect Fail")

			LPD3DXFILEDATA pFileData=NULL;
		SIZE_T size = 0;
		pEnumObj->GetChildren(&size);
		for(SIZE_T i = 0;i<size;++i)
		{
			if(SUCCEEDED(pEnumObj->GetChild(i,&pFileData)))
			{

				if(FAILED(ParserObject(pFileData,NULL)))
				{
					SafeRelease(pFileData);
					break;
				}

				SafeRelease(pFileData)

			}


		}

		if(m_pEntity3D!=NULL)
			m_pEntity3D->updataSkeletonAndAnimationset();
	

		SafeRelease(pEnumObj);


		return X_OK;
	}



	HRESULT xParser::ParserObject(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParentData)
	{
		if(pFileData==NULL)
			return X_FAIL;

		if(pFileData->IsReference())
			return X_OK;

		ImplementParserObj(pFileData,pParentData);

		SIZE_T childSize = 0;
		LPD3DXFILEDATA pChildData = NULL;
		pFileData->GetChildren(&childSize);
		for(SIZE_T i = 0;i<childSize;++i)
		{
			if(SUCCEEDED(pFileData->GetChild(i,&pChildData) ) )
			{
				ParserObject(pChildData,pFileData);
			}
			SafeRelease(pChildData);
		}

		//SafeRelease(pFileData)

		return X_OK;
	}


	HRESULT xParser::ImplementParserObj(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParentData)
	{


		//return X_OK;
		xString name = GetDataName(pFileData);
		const GUID* pid = GetDataType(pFileData);
		if(pid==NULL)
			return X_FAIL;

		if(*pid==TID_D3DRMFrame)
		{
		
			ParserFrame(pFileData,pParentData);

		}else if( *pid==TID_D3DRMMesh)
		{
			//return X_OK;
			//LPD3DxMesh pMesh=NULL;
			ParserMesh(pFileData);
		}else if(*pid==TID_D3DRMFrameTransformMatrix )
		{
			ParserFrameTransformMatrix(pFileData);
		}else if(*pid==DXFILEOBJ_XSkinMeshHeader)
		{
			ParserSkin(pFileData,pParentData);

		}else if(*pid==TID_D3DRMAnimationSet)
		{
			ParserAnimationSet(pFileData,pParentData);
		}else if(*pid==TID_D3DRMAnimation)
		{
			ParserAnimation(pFileData,pParentData);
		}else if(*pid==TID_D3DRMAnimationKey)
		{//
			ParserAnimationKey(pFileData,pParentData);
		}else if(*pid==gFrameMeshName)
		{

			ParserSceneMesh(pFileData,pParentData);
		}else if(*pid==TID_D3DRMMaterial)
		{
			ParserMaterial(pFileData,pParentData);
		}else if(*pid==TID_D3DRMTextureFilename)
		{
			ParserTexture(pFileData,pParentData);
		}else if(*pid==DXFILEOBJ_EffectInstance)
		{
			ParserEffect(pFileData,pParentData);
		}else if(*pid==DXFILEOBJ_EffectParamFloats||*pid==DXFILEOBJ_EffectParamString\
			||*pid==DXFILEOBJ_EffectParamDWord)
		{
			ParserEffectParameter(pFileData,pParentData);
		}

		return NULL;
	}


	HRESULT xParser::ParserFrameTransformMatrix(LPD3DXFILEDATA pFileData)
	{
		if(pFileData==NULL)
			return X_FAIL;

		DWORD DataLeng=0;
		WORD* pData=NULL;
		xString name = GetDataName(pFileData);
		//xString Parentname = GetDataName(pParent);
		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserFrameTransformMatrix")
			if(DataLeng!=0&&pData!=NULL)
			{

				// SceneFrame
				//xBone* pBone = m_pSkeleton->GetBoneByName(name);
				if(m_pCurrentBone!=NULL)
				{
					//xMatrix pMat = *((xMatrix*)pData);
					if(m_pCurrentBone!=NULL)
					{
						m_pCurrentBone->SetOffsetMatrix(*((xMatrix*)pData));

						m_pCurrentBone->SetLocalMatrix(*((xMatrix*)pData));
					}

				}


			}

			pFileData->Unlock();

			return X_OK;
	}

	/*HRESULT  xParser::ParserChildObject(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{





	return X_FAIL;

	}*/


	HRESULT  xParser::ParserFrame(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		if(pFileData==NULL)
			return X_FAIL;
		if(*GetDataType(pFileData)==TID_D3DRMFrame)
		{
		   if(pFileData->IsReference())
			{
				return X_FAIL;
			}
			DWORD DataLeng=0;
			WORD* pData=NULL;
			xString name = GetDataName(pFileData);
			if(name.length()==0)
			{
				static int cc = 0;
				name ="unNameNode";
				name+=xHelp::IntToString(cc++);
			}
			xString Parentname = GetDataName(pParent);
			
		
			if(m_pEntity3D==NULL)
			{
				m_pEntity3D = XEngine::getSingleton().GetMainSceneManager()->CreateEntity(name,"ss");
				XEngine::getSingleton().GetMainSceneManager()->CreateSceneNode()->AttachRenderObject(m_pEntity3D);
				//m_pEntity3D->GetAttachSceneNode()->EnableRender(true);
			}else
			{
				m_pCurrentEntity = XEngine::getSingleton().GetMainSceneManager()->CreateEntity(name,"ss");

			}
			if(m_pSkeleton.isNull())
			{
				//m_pSkeleton = new xSkeleton();
				xResourceCreateParameter SkeletonPara("name");
				m_pSkeleton =xSkeletonManager::getSingleton().CreateResource(&SkeletonPara);
				if(m_pEntity3D!=NULL)
					m_pEntity3D->SetSkeleton(m_pSkeleton);
			

			}


			if( m_pSkeleton->GetRootBone()==NULL)
			{
				m_pCurrentBone= 	m_pSkeleton->AddRootBone(name);
		
			}else
			{
				m_pCurrentBone= m_pSkeleton->AddBone(name,Parentname);

			}
			if(m_pCurrentEntity!=NULL&&m_pEntity3D!=NULL&&m_pCurrentBone!=NULL)
			{
				xVector3 pos(0.0f,0.0f,0.0f);
				xQuaternion quate(0.0f,0.0f,0.0f,1.0f);
				m_pEntity3D->AttachTagPoint(m_pCurrentBone->GetName(),m_pCurrentEntity,pos,quate);
			}
	
		}
		return X_OK;
	}


	xString	xParser::GetDataName(LPD3DXFILEDATA pFileData)
	{
		xString	 name;
		if(pFileData==NULL)
			return name;
		DWORD nameLen;
		pFileData->GetName(NULL,&nameLen);
		if(nameLen==0)
			return xString();
		char* pname =new char[nameLen+1];
		pFileData->GetName(pname,&nameLen);
		name = pname;
		delete [] pname;
		return name;
	}

	const GUID* xParser::GetDataType(LPD3DXFILEDATA pFileData)
	{
		if(pFileData==NULL)
			return NULL;
		static GUID pid;
		pFileData->GetType(&pid);
		return &pid;
	}


	HRESULT xParser::ParserMesh(LPD3DXFILEDATA pFileData)
	{
		if(pFileData==NULL||*(GetDataType(pFileData))!=TID_D3DRMMesh)
			return X_FAIL;

		

		DWORD DataLeng = 0; 
		char* pData=NULL;
		LPDIRECT3DDEVICE9 pDevice =(LPDIRECT3DDEVICE9)(XEngine::getSingleton().GetRenderSystem()->GetRenderDevice());
		DWORD MaterialNumber = 0;
		LPD3DXBUFFER pMatbuff,pEffectBuffer,pAdjectBuuffer;
		LPD3DXMESH pMesh=NULL;

		HRESULT hr =  D3DXLoadSkinMeshFromXof(pFileData,D3DXMESH_MANAGED,\
			pDevice,&pAdjectBuuffer,&pMatbuff,&pEffectBuffer,&MaterialNumber,&m_pSkinInfor,&pMesh);

		//HRESULT hr =  D3DXLoadMeshFromXof(pFileData,D3DXMESH_MANAGED,\
		pDevice,&pAdjectBuuffer,&pMatbuff,&pEffectBuffer,&MaterialNumber,&pMesh);

		SafeRelease(pAdjectBuuffer)
		SafeRelease(pMatbuff)
		SafeRelease(pEffectBuffer)



		xString name =  GetDataName(pFileData);

		DWORD leng=0;
		DWORD* pdata=NULL;
		if(SUCCEEDED(pFileData->Lock(&leng,(LPCVOID*)&pdata)))
		{
			if(leng!=0&&pdata!=NULL)
			{
				// 	m_pMesh = static_cast<xMesh*>(xMeshManager::getSingleton().CreateResourceFormMemory(name,(char*)pdata,leng));
				m_pMesh = xMeshManager::getSingleton().CreateMeshByxMesh(pMesh);
				if(m_pMesh.isNull()==false&&m_pSkinInfor!=NULL&&m_pSkinInfor->GetNumBones()!=0)
				{
					if(SUCCEEDED(m_pMesh->CreateSkin(m_pSkinInfor->GetNumBones())))
					{
						m_pMesh->GetSkin()->SetSkinData(m_pSkinInfor);

						int BoneUn = m_pMesh->GetSkin()->GetBoneCount();

						m_pMesh->GeneranteSkinMesh();
					}

				}
			}
			pFileData->Unlock();
		}
		if(m_pMesh.isNull()==false&&m_pEntity3D!=NULL&&m_pCurrentEntity==NULL)
		{
			m_pEntity3D->SetMesh(m_pMesh);
		}else if(m_pMesh.isNull()==false&&m_pCurrentEntity!=NULL)
		{
			m_pCurrentEntity->SetMesh(m_pMesh);
		}


		return X_OK;

	}


	HRESULT xParser::ParserSkin(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		return X_OK;
	}



	HRESULT xParser::ParserAnimationSet(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{

		if(m_pAnisetCollect==NULL)
		{
			m_pAnisetCollect = new xAnimationSetCollect();
			if(m_pEntity3D!=NULL)
                     m_pEntity3D->SetAnimationSetCollect(m_pAnisetCollect);
      	}

		xString Setname = GetDataName(pFileData);
		if(Setname.length()==0)
		{
			m_pCurrentAniSet=	 m_pAnisetCollect->CreateAnimationSet();
		}else
		{
			m_pCurrentAniSet =  m_pAnisetCollect->CreateAnimationSet(Setname);
		}
	
		return X_OK;
	}

	HRESULT xParser::ParserAnimation(LPD3DXFILEDATA pFileDat,LPD3DXFILEDATA pParent)
	{
		if(m_pCurrentAniSet==NULL)
		{
			return X_FAIL;
		}

		SIZE_T childCount = 0;
		pFileDat->GetChildren(&childCount);
		xString Aniname;
		LPD3DXFILEDATA pChildData = NULL;
		for(SIZE_T i = 0;i<childCount;++i)
		{
			if(SUCCEEDED(pFileDat->GetChild(i,&pChildData)))
			{
				if(pChildData->IsReference())
				{
					const GUID* pid =  GetDataType(pChildData);
					if(pid!=NULL&&*pid ==TID_D3DRMFrame )
					{
						Aniname = GetDataName(pChildData);
                         SafeRelease(pChildData)
						break;
					}

				}
			
			}
	      SafeRelease(pChildData)
		}
		if(Aniname.empty())
		{
			static int Aniint = 0;
			Aniname = "unNameAnimation";
			Aniname+=xHelp::IntToString(Aniint++);
		}

		m_pCurrentAni = m_pCurrentAniSet->CreateNodeAnimation(Aniname);

		return X_OK;	
	}

	HRESULT xParser::ParserAnimationKey(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		if(m_pCurrentAni==NULL)
			return X_FAIL;

		DWORD leng=0;
		DWORD* pdata=NULL;
		if(SUCCEEDED(pFileData->Lock(&leng,(LPCVOID*)&pdata)))
		{
			if(leng!=0&&pdata!=NULL)
			{
				DWORD keytype = *pdata; ++pdata;
				DWORD keyCount = *pdata;++pdata;

				for(DWORD i = 0;i<keyCount;++i)
				{

					DWORD time=*pdata++; 
					DWORD valueCount = *pdata++; 
					switch (keytype)
					{
					case 0://rotation
						{
							xQuaternion quater =*((xQuaternion*)pdata);
							pdata+=valueCount;
							m_pCurrentAni->AddRotationKey((float)time,quater);

							break;
						}
					case 1://----scale
						{
							xVector3 scale = *((xVector3*)pdata);
							pdata+=valueCount;
							m_pCurrentAni->AddScaleKey((float)time,scale);
							break;
						}
					case 2://-poston
						{
							xVector3 pos = *((xVector3*)pdata);
							pdata+=valueCount;
							m_pCurrentAni->AddPositionKey((float)time,pos);
							break;

						}
					case 4://------matrix
						{
							xMatrix mat =*((xMatrix*)pdata);
							m_pCurrentAni->AddMatrixKey((float)time,mat);
							pdata+=valueCount;
							break;
						}
					default:
						{
							break;
						}
					}

				}

			}

			pFileData->Unlock();
		}

		return X_OK;
	}



	HRESULT  xParser::ParserSceneMesh(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		if(pFileData==NULL)
			return X_FAIL;

		DWORD DataLeng=0;
		DWORD* pData=NULL;
		xString name = GetDataName(pFileData);
		xString ParaName = GetDataName(pParent);
		//xString Parentname = GetDataName(pParent);
		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserFrameMesh")
			if(DataLeng!=0&&pData!=NULL)
			{

				WORD passCount = *pData++;
				xString meshFile = (LPSTR)pData;
				if(name.empty())
					name = meshFile;


				xMeshPtr pMesh = xMeshManager::getSingleton().GetByName(name);
				if(pMesh.isNull())
				{
					pMesh =xMeshManager::getSingleton().CreateResource(name,meshFile);

				}

				xSceneManager* pSceneManager =  XEngine::getSingleton().GetMainSceneManager();
				static int cc = 0;
				xString EnityName = meshFile+xHelp::IntToString(cc++);
				xEntity3D* pTemEntity = pSceneManager->CreateEntity(EnityName,name);

				//	xSceneNode* pNode = pSceneManager->CreateSceneNode();

				if(meshFile.find("sky")!=xString::npos)
			    {
				 pTemEntity->SetRenderQueueLevel(xRQL_BAK_SKY_LEVEL);
			    }

				if(pTemEntity!=NULL&&m_pCurrentBone!=NULL&&m_pEntity3D!=NULL)
				{
					xVector3 pos;
					xQuaternion que;
					m_pEntity3D->AttachTagPoint(m_pCurrentBone->GetName(),pTemEntity,pos,que);

			     }


			}

			pFileData->Unlock();

			return X_OK;

	}


	HRESULT xParser::ParserMaterial(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{

		DWORD DataLeng=0;
		float * pData=NULL;
		xString name = GetDataName(pFileData);

		if(name.empty())
		{
			static int MaterialIndex = 0;
			xString temName = "unNamematerial";
			temName+=xHelp::IntToString(MaterialIndex);
			m_pMaterial = xMaterialManager::getSingleton().GetByName(temName);
			while(m_pMaterial.isNull()==false)
			{
				++MaterialIndex;
				temName+=xHelp::IntToString(MaterialIndex);
				m_pMaterial =xMaterialManager::getSingleton().GetByName(temName);
			}
			name = temName;

		}

			xResourceCreateParameter matCratePar(name);
			 m_pMaterial = xMaterialManager::getSingleton().CreateResource(&matCratePar);
			 if(m_pMaterial.isNull()==false)
			 {
				 m_pRenderScheme= m_pMaterial->CreateRenderScheme(10,xShaderPtr());
			 }
			 
			 if(m_pEntity3D!=NULL&&m_pCurrentEntity==NULL)
			 {
				 size_t count =  m_pEntity3D->GetMaterialCount();
				 m_pEntity3D->SetMaterial(count,m_pMaterial);
				 if(m_pMesh.isNull()==false)
				 {
					 m_pMesh->SetSubMeshMaterial(count,m_pMaterial);
				 }
			 }



		
		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserFrameMesh")

			if(pData!=NULL&&DataLeng)
			{
				xColor diffcolor;
				diffcolor.r = *pData++;
				diffcolor.g = *pData++;
				diffcolor.b = *pData++;
				diffcolor.a = *pData++;

				float power = *pData++;

				xColor specularColor;
				specularColor.r =*pData++; 				specularColor.g =*pData++; 	
				specularColor.b =*pData++; 				specularColor.a =1.0f;

				xColor emissiveColor;

				emissiveColor.r =*pData++; 				emissiveColor.g =*pData++; 	
				emissiveColor.b =*pData++; 				emissiveColor.a =1.0f;

				int ccc  = 0;
				ccc++;

			}

			pFileData->Unlock();

			return X_OK;
	}


	HRESULT xParser::ParserTexture(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		if(pFileData==NULL||m_pRenderScheme==NULL)
			return X_FAIL;
		DWORD DataLeng=0;
		float * pData=NULL;

		xString name = GetDataName(pFileData);
		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserFrameMesh")

			if(pData!=NULL&&DataLeng!=0)
			{
				xString filename =(char*)pData;
				if(name.empty())
					name = filename;
				xTexturePtr	 pTexture = xTextureManager::getSingleton().CreateResource(name,filename);
				if(pTexture.isNull()==false)
				{
					m_pRenderScheme->SetTexture(m_pRenderScheme->GetTexutreNumber(),pTexture);
				}

			}
			pFileData->Unlock();
			return X_OK;
	}

	HRESULT xParser::ParserEffect(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{
		DWORD DataLeng=0;
		float * pData=NULL;
		if(m_pRenderScheme==NULL)
			return X_FAIL;

		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserFrameMesh")

			if(pData!=NULL&&DataLeng!=0)
			{
				xString filename =(char*)pData;
				xString name = GetDataName(pFileData);
				if(name.empty())
				{
					name = filename;
				}

				xShaderPtr  pShader =xShaderManager::getSingleton().GetByName(name);
				if(pShader.isNull())
				{
					pShader =xShaderManager::getSingleton().CreateResource(name,filename);
				}

				m_pRenderScheme->SetShader(pShader);
			}
			pFileData->Unlock();
			return X_OK;
	}



	HRESULT xParser::BuildMeshMaterial(DWORD MatNum,LPD3DXBUFFER pMatbuff,LPD3DXBUFFER pEffectBuffer,LPD3DXBUFFER pAdjectBuuffer)
	{
		if(m_pMesh.isNull())
			return X_FAIL;
		D3DXMATERIAL* pd3dxmat = ( D3DXMATERIAL* )pMatbuff->GetBufferPointer();
		LPD3DXEFFECTINSTANCE pEffectInstance =(LPD3DXEFFECTINSTANCE) pEffectBuffer->GetBufferPointer();
		DWORD size  = pEffectBuffer->GetBufferSize();
		DWORD presize = sizeof(D3DXEFFECTINSTANCE);

		for(DWORD i = 0;i<MatNum;++i)
		{
			char* fileName = pd3dxmat[i].pTextureFilename;



			D3DXEFFECTINSTANCE pEff =  pEffectInstance[i];
			if(pEff.pEffectFilename!=NULL)
			{

			}


		}

		return X_OK;

	}


	HRESULT xParser::ParserEffectParameter(LPD3DXFILEDATA pFileData,LPD3DXFILEDATA pParent)
	{

		const GUID* pid = GetDataType(pFileData);
		if(pid==NULL||m_pRenderScheme)
			return X_FAIL;

		DWORD DataLeng=0;
		void * pData=NULL;

		DWORD childsize = 0;
		pFileData->GetChildren(&childsize);
		FL(pFileData->Lock(&DataLeng,(LPCVOID*)&pData),"FAIL:ParserEffectParameter")

			if(DataLeng==0||pData==NULL)
			{
				pFileData->Unlock();
				return X_FAIL;
			}

			char* pParametername=NULL;
			void* pValue =NULL;
			UINT ValueSize = 0;
			if(*pid==DXFILEOBJ_EffectParamString)
			{

				pParametername=(char*)pData;
				int leng = strlen(pParametername);
				char* pParameterValue=NULL;
				if(leng<DataLeng-1)
				{
					pParameterValue=((char*)pData)+leng+1 ;
				}

				m_pRenderScheme->SetConstantValue(pParametername,pParameterValue,strlen(pParameterValue));

			}else if(*pid==DXFILEOBJ_EffectParamFloats)
			{

				pParametername = (char*)pData;
				int leng =strlen(pParametername);
				DWORD floatCount = *((DWORD*)(((char*)pData)+leng+1));

				// std::vector<float> pValue;
				// pValue.resize(floatCount);
				// memcpy(&(pValue[0]),((char*)pData)+leng+1+sizeof(DWORD),sizeof(float)*floatCount);

				m_pRenderScheme->SetConstantValue\
					(pParametername,((char*)pData)+leng+1+sizeof(DWORD),sizeof(float)*floatCount);

			}else if(*pid==DXFILEOBJ_EffectParamDWord)
			{
				pParametername = (char*)pData;
				int leng =strlen(pParametername);
				DWORD value = *(DWORD*)((char*)(pData)+leng+1);

				m_pRenderScheme->SetConstantValue(pParametername,value);
			}

			pFileData->Unlock();



			return X_OK;

	}

}